package com.hoki.zj.hrm.service.impl;

import com.hoki.zj.hrm.asserts.GlobalAssert;
import com.hoki.zj.hrm.doc.CourseDoc;
import com.hoki.zj.hrm.interceptor.utils.ESSearchBucketsList;
import com.hoki.zj.hrm.mapping.HighlightResultMapper;
import com.hoki.zj.hrm.query.CourseSearchQuery;
import com.hoki.zj.hrm.repository.CourseElasticsearchRepository;
import com.hoki.zj.hrm.service.ICourseESService;
import com.hoki.zj.hrm.vo.AggsVo;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class CourseESServiceImpl implements ICourseESService {

    @Autowired
    private CourseElasticsearchRepository courseEsRepository;

    @Autowired
    private ElasticsearchTemplate template;

    @Autowired
    private HighlightResultMapper resultMapper;

    /**
     * 1.ElasticSearch添加/修改
     * @param courseDoc CourseDoc courseDoc
     */
    @Override
    public void save(CourseDoc courseDoc) {
        // 判断非空
        GlobalAssert.isNull(courseDoc.getId(), "课程文档对象为空!");
        courseEsRepository.save(courseDoc);
    }

    /**
     * 2.ElasticSearch删除
     * @param id コースid
     */
    @Override
    public void del(Long id) {
        courseEsRepository.deleteById(id);
    }

    /**
     * 3.ElasticSearch 前台页面列表展示(分页/高级)
     * @return list
     */
    @Override
    public ESSearchBucketsList search(CourseSearchQuery courseSearchQuery) {
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        // 1.构建查询条件
        setSearchQueryCondition(courseSearchQuery, queryBuilder);
        // 2.高亮查询 high-light-search
        setHighlightFileds(queryBuilder);
        /*{
            "aggs": {
                "gradeName": {
                    "terms": {
                        "field": "gradeName",
                         "size": 10
                    }
                },
                "tenantName": {
                    "terms": {
                        "field": "tenantName",
                        "size": 10
                    }
                }
            }
        }*/
        // 3.聚合查询
        setAggrSerach(queryBuilder);
        // 4.使用ElasticsearchTemplate,查询数据
        AggregatedPage<CourseDoc> searchResult = template.queryForPage
                (queryBuilder.build(), // SearchQuery
                        CourseDoc.class, // the class of ElasticSearch XxxDoc
                        resultMapper);// SearchResultMapper
        // .构造聚合查询结果
        Map<String, List<AggsVo>> aggsResult = new HashMap<>();
        // 5.获取聚合查询的所有结果
        Aggregations aggregations = searchResult.getAggregations();
        List<Aggregation> aggsList = aggregations.asList(); // 转为list
        // 循环获取数据,设置聚合查询的结果((StringTerms) aggregations.asList().get(0)).getBuckets().get(0)
        setAggsResult(aggsResult, aggsList);

//        Page<CourseDoc> searchResult = courseEsRepository.search(queryBuilder.build());
        // .返回一个ESSearchBucketsList结果集(传回数据和数据总数量, 和封装的聚合查询结果)
        return new ESSearchBucketsList<>(searchResult.getTotalElements(), searchResult.getContent(), aggsResult);
    }

    /**
     * 设置聚合查询的结果
     */
    private void setAggsResult(Map<String, List<AggsVo>> aggsResult, List<Aggregation> aggsList) {
        aggsList.forEach(aggregation -> {
            List<AggsVo> aggsVoList = new ArrayList<>(); // 构造value
            List<StringTerms.Bucket> buckets = ((StringTerms) aggregation).getBuckets(); // get buckets
            String name = aggregation.getName(); // 获取key
            buckets.forEach(bucket -> {
                // 构造单个对象
                AggsVo aggsVo = new AggsVo((String)bucket.getKey(), bucket.getDocCount());
                // 设置aggsVoList
                aggsVoList.add(aggsVo);
            });
            aggsResult.put(name, aggsVoList);
        } );
    }

    /**
     * 聚合查询
     */
    private void setAggrSerach(NativeSearchQueryBuilder queryBuilder) {
        // aggs: -> name:"gradeName" -> AGG_TYPE:terms -> field & size
        // 等级名聚合
        queryBuilder.addAggregation
                (AggregationBuilders.terms("gradeName").field("gradeName").size(10));
        // 机构名聚合
        queryBuilder.addAggregation
                (AggregationBuilders.terms("tenantName").field("tenantName").size(10));
    }

    /**
     * 高亮字段设置
     */
    private void setHighlightFileds(NativeSearchQueryBuilder queryBuilder) {

        HighlightBuilder.Field field = new HighlightBuilder.Field
                ("name"). // 指定高亮的字段
                preTags("<em style='color: red'>"). // 指定格式
                postTags("</em>");
        queryBuilder.withHighlightFields(field);
    }

    /**
     * 构建查询条件
     */
    private void setSearchQueryCondition(CourseSearchQuery courseSearchQuery,
                                         NativeSearchQueryBuilder queryBuilder) {
        // 指定查询的索引
        queryBuilder.withIndices("hrm");
        // 指定查询类型
        queryBuilder.withTypes("course");
        // 分页{@link PageRequest}. Pages are zero indexed
        queryBuilder.withPageable
                (PageRequest.of(courseSearchQuery.getPage() - 1,
                        courseSearchQuery.getRows()));
        // 排序
        String sortField = "saleCount";
        if (StringUtils.hasText(courseSearchQuery.getSortField())) {
            switch (courseSearchQuery.getSortField()) {
                case "xl":
                    sortField = "saleCount"; break;
                case "xp":
                    sortField = "publishTime"; break;
                case "pl":
                    sortField = "commentCount"; break;
                case "jg":
                    sortField = "price"; break;
                case "rq":
                    sortField = "viewCount"; break;
            }
        }
        String sortType = courseSearchQuery.getSortType(); // 获取排序方式
        SortOrder order = SortOrder.ASC; // 默认升序
        if (StringUtils.hasText(sortType) && sortType.equalsIgnoreCase("desc")) { // 降序
            order = SortOrder.DESC;
        }
        queryBuilder.withSort(SortBuilders.fieldSort(sortField).order(order));

        // 构建查询条件
        // bool
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // must -> match -> CourseDoc.name keyword
        if (StringUtils.hasText(courseSearchQuery.getKeyword())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("name", courseSearchQuery.getKeyword()));
        }

        // filter -> term -> courseTypeId
        if (courseSearchQuery.getCourseTypeId() != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("courseTypeId", courseSearchQuery.getCourseTypeId()));
        }

        if (courseSearchQuery.getTenantName() != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("tenantName", courseSearchQuery.getTenantName()));
        }

        if (courseSearchQuery.getGradeName() != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("gradeName", courseSearchQuery.getGradeName()));
        }

        // filter -> term -> price
        if (courseSearchQuery.getPriceMin() != null) {
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(courseSearchQuery.getPriceMin()));
        }
        // filter -> term -> price
        if (courseSearchQuery.getPriceMax() != null) {
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").lte(courseSearchQuery.getPriceMax()));
        }

        queryBuilder.withQuery(boolQueryBuilder);
    }

}
